// 在根目录创建一个plugins编写vite插件帮我们启动electron
// 编写electron开发模式

// 导入需要使用的类型和库
import type { Plugin } from 'vite'
import type { AddressInfo } from 'net'
import { spawn } from 'child_process'
import fs from 'node:fs'
import { buildSync } from 'esbuild'
import electronPath from 'electron'

// 引入createRequire函数
// 是 Node.js 模块系统中的一个函数，目的可以在 ES 模块中使用 require 来加载 CommonJS 模块。
// import { createRequire } from 'module'
// const require = createRequire(import.meta.url)

// 导出vite插件函数
export const viteElectronDev = (): Plugin => {
  return {
    // 插件名称
    name: 'vite-electron-dev',
    // 配置vite插件钩子
    // 在configureServer钩子函数中实现插件逻辑
    // 为什么选这个钩子函数呢？因为这个钩子函数会在vite的HTTP服务器启动之后执行，这样我们就可以在这个钩子函数中启动Electron进程。
    configureServer(server) {
      // 定义初始化Electron应用程序的函数
      const initElectronApp = async () => {
        // 使用esbuild编译Typescript代码为Javascript代码
        // 为什么要编译成js。因为electron不认识ts，所以需要编译成js
        // require('esbuild').buildSync({})  改成 ESModule规范引入buildSync函数
        buildSync({
          entryPoints: ['src/background.ts'], // 指定要编译的入口文件吗
          bundle: true, // 指定是否打包所有依赖项
          outfile: 'dist/background.js', // 指定输出文件的路径和名称
          platform: 'node', // 指定编译的目标平台
          target: 'node12', // 指定编译的目标JavaScript版本,这里是Node.js 12，表示编译为Node.js 12及以上版本可用的代码。
          external: ['electron'], //指定不需要被打包的外部依赖项，这里是['electron']，表示electron模块不需要被打包。
          format: 'esm', //指定输出文件的格式为ES Module;eletron版本>=30强制启用ESM支持！ 并且不再支持 CommonJS 模块。Nodejs 模块化规范遵循两套一 套CommonJS规范另一套esm规范;
        })
      }

      // 调用初始化Electron应用程序函数
      initElectronApp()

      // 监听vite的HTTP服务器的listening事件
      server.httpServer?.once('listening', () => {
        // 获取vite服务信息
        // 获取HTTP服务器的监听地址和端口号
        const addressInfo = server?.httpServer?.address() as AddressInfo
        // console.log(addressInfo, 'addressInfo')
        // 拼接IP地址 给electron 启动服务的时候要用
        const IP = `http://localhost:${addressInfo.port}`
        // console.log(IP, 'IP')

        // 启动Electron进程。
        // spawn函数用于启动一个子进程，并返回一个子进程对象（信息）。
        // let electronProcess = spawn(require('electron'), ['dist/background.js', IP])
        // spawn函数解析 ：
        // 第一个参数是electron的入口文件。electronPath或者require('electron')返回的是一个路径
        // 第二个参数目的把本地入口文件传给electron，这里是['dist/background.js', IP]，表示启动electron进程并传入IP参数。注意electron不支持ts,所以是background.js，并且要在初始化应用函数initElectronApp中做好background.ts编译成js
        // 进程传参数发送给electron进程。background.ts怎么知道这个IP呢？利用process对象获取。详情见background.ts
        let electronProcess = spawn(electronPath + '', ['dist/background.js', IP])
        // console.log(electronProcess, 'electronProcess')

        // 监听主进程的代码更改--里面的代码主要是实现热更新
        fs.watchFile('src/background.ts', () => {
          // 杀死当前的Electron进程
          electronProcess.kill()
          // 重新编译主进程代码并重新启动Electron进程
          initElectronApp()
          // electronProcess = spawn(require('electron'), ['dist/background.js', IP])
          electronProcess = spawn(electronPath + '', ['dist/background.js', IP])
        })

        //监听electron进程的日志 stdout输出
        electronProcess.stdout?.on('data', (data) => {
          // console.log('日志：' + data.toString())
        })
      })
    },
  }
}

// 问题回顾：
// 根据up主掘金笔记敲了一遍，发起启动时报错
// Error: Dynamic require of "file:///Users/young/Documents/learning/learn_interview_2024/learn/3-Electron/base/electron-demo/electron-app/node_modules/_esbuild@0.24.2@esbuild/lib/main.js" is not supported
// 原因：检查一下你的electron版本。从 Electron 30 开始，Electron 强制启用了 ES 模块支持，并且不再支持 CommonJS 模块。因此，你需要确保你的代码和依赖项都使用 ES 模块。【百度+ds满血版问的】
// 解决：
// 检查require引入的模块，替换成import引入。
// 1. 引入buildSync函数
// import { buildSync } from 'esbuild'
// buildSync({
// ...原来的配置
// format: 'esm' //指定输出文件的格式为ES Module，如果不配置就是报错，dist/background.js是使用CommonJS规范引入模块的
// })
// 2.require('electron')
// 方案1：引入createRequire函数，使你可以在 ES 模块中使用 require 来加载 CommonJS 模块。
// import { createRequire } from 'module';
// const require = createRequire(import.meta.url);
// 依然可使用CommonJS规范 require引入electron
// let electronProcess = spawn(require('electron'), 【'dist/background.js', IP】)
// 方案2：引入electron模块 【个人建议使用这种方案，统一风格】
// import electronPath from 'electron'
// let electronProcess = spawn(electronPath + '', 【'dist/background.js', IP】)
// 无论采用方案1还是方案2，都必须在buildSync函数options对象中指定输出文件的格式为ES Module：即配置 format: 'esm'
// 哪种方案更好？
// 方案1：createRequire函数，在ES模块中引入CommonJS模块。方便引入其他模块，即：不支持esm规范的模块，可继续使用CommonJS规范引入。扩展性可能更好
// 方案2：直接使用esm规范引入electron模块。统一风格，可读性更好！并且文件中暂时没有不支持esm规范的模块，不需要createRequire函数。
// 只有在确实需要使用 require 并且 electron 模块不支持 ES 模块导入的情况下，才建议使用 createRequire 函数。
// 3.githua-copilot建议确保你的 tsconfig.json 文件配置支持 ES 模块
// {
//   "compilerOptions": {
//     "module": "ESNext",
//     "target": "ESNext",
//     "moduleResolution": "node",
//     "esModuleInterop": true,
//     "allowSyntheticDefaultImports": true
//   }
// }
